/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/
/*
Authors: Ram Ovadia 201374022 and Lior Fass 300069705
Project: HW Assignment 2 
Description: This is the module holding the main function  
*/
/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "VS_utils.h"

int main(int argc, char *argv[])
{
	int i,j,k=0;
	int timeout = -1;
	FILE * hInFile = NULL;
	FILE * hOutFile = NULL;
	char line[512];
	order_t order = serial;
	char * command = NULL,*sInputFile = NULL;
	PROCESS_INFORMATION ProcInfo[NUM_OF_EXES];
	HANDLE lpHandles[NUM_OF_EXES]; 
	DWORD lpExitCode = 0; 
	BOOL timeout_exceeded[NUM_OF_EXES] = {0,0,0,0};
	int retVal;
	BOOL passFlag = TRUE;
	BOOL badFileFlag = FALSE;
	clock_t startTime, endTime, elapsed_time;

	
	//test number of arguments
	if (argc != 5)
	{
		printf("wrong number of arguments\r\n");
		return 1;
	}
	
	//test order
	if ( (strlen(argv[ORDER_ARG]) == strlen(serial_str)) && (strcmp(argv[ORDER_ARG], serial_str) == 0) )
	{
		order = serial;
	} 
	else if ( (strlen(argv[ORDER_ARG]) == strlen(parallel_str)) && (strcmp(argv[ORDER_ARG], parallel_str) == 0) )
	{
		order = parallel;
	}
	else
	{
		printf("invalid order specified\r\n");
		return 1;
	}

	//test valid positive timeout
	if ( (!isInteger(argv[TIMEOUT_ARG])) || (0 >= (timeout = atoi(argv[TIMEOUT_ARG]))) )
	{
		printf ("invlaid or zero timeout specified\r\n");
		return 1;
	}

	//test input file exists and is .txt
	if (argv[INPUT_ARG]==NULL || (strlen(argv[INPUT_ARG]) <= 4))
	{
		printf("Invalid input file specified\r\n");
		return 1;
	} 
	else 
	{
		sInputFile = (char *)malloc(sizeof(char)*(strlen(argv[INPUT_ARG])+1)); 
		strcpy(sInputFile,argv[INPUT_ARG]);
		StringToLower(sInputFile);
		if ( (isFile(argv[INPUT_ARG]) != TRUE) || ( strcmp( txtfile_str, sInputFile + sizeof(char)*( strlen(sInputFile) - strlen(txtfile_str) ) ) != 0 )  )
		{
			printf("input file is not a valid .txt file\r\n");
			return 1; 
		}
		free(sInputFile);
	}

	//printf("DEBUG: in %s, in %s, input path = %s, order = %d, timeout = %d\r\n", "VersionControl.c", "main", argv[INPUT_ARG], order, timeout);
	

	//open inputfile to iterate over each line, test for valid file path, and run tests.
	hInFile = fopen(argv[INPUT_ARG], "r");
	if (hInFile == NULL)
	{
		printf("Failed to open input file\r\n");
		return 1;
	} 

	//open outputfile.
	hOutFile = fopen(argv[OUTPUT_ARG], "w");
	if (hOutFile == NULL)
	{
		printf("Failed to open output file\r\n");
		return 1;
	} 

	//initialize ProcInfoArray
	initializeProcInfo(ProcInfo);
	
	//get time reference for bonus.
	startTime = clock();
	
	printf("Running %s test...\r\n", ( order==serial ? "serial" : "parallel"));

	//iterate over list of paths in input file. 
	while (fscanf(hInFile, "%s\r\n", &line) != EOF)
	{
		//create a process for every tester on each file in the list 
		for (j=0; j<NUM_OF_EXES; j++)
		{ 
			//build the command.
			command = BuildCommand(j,line);

			//create the process
			if (!CreateProcessSimple(command, &ProcInfo[j]))
			{
				printf("failed to create process of command: %s\r\n", command);
			}

			if (order == serial){
				retVal = WaitForSingleObject(ProcInfo[j].hProcess, timeout);
				if (retVal == WAIT_TIMEOUT)
				{
					timeout_exceeded[j] = 1;
					if (!TerminateProcess(ProcInfo[j].hProcess, 3))
					{
						printf("failed to terminate process exes[j]\r\n");
					}
				}
				else if (retVal == WAIT_FAILED)
				{
					printf("FATAL ERROR\r\n");
					closeAllHandles(ProcInfo, timeout_exceeded, lpHandles);
					finalizeRun(FALSE, hInFile, hOutFile);
					return 1;
				}
			}
			else
			{
				//in parallel mode, create an array refering to the process handles =, later used in WaitForMultipleObects 
				lpHandles[j] = ProcInfo[j].hProcess;
			}
			free(command);
		}

		if (order==parallel)
		{
			retVal = WaitForMultipleObjects(NUM_OF_EXES, lpHandles, 1, timeout);
			if (retVal == WAIT_TIMEOUT)
			{
				//check which one of the processes timed-out for proper output, by polling on status with waitForSingleObject with 0 timeout. 
				for (i=0; i<NUM_OF_EXES; i++)
				{
					retVal = WaitForSingleObject(ProcInfo[i].hProcess, 0);
					if (retVal == WAIT_TIMEOUT)
					{
						timeout_exceeded[i] = 1;
						if (!TerminateProcess(ProcInfo[i].hProcess,3))
						{
							printf("failed to terminate process %s\r\n", exes[i]);
						}
					}
					else if (retVal == WAIT_FAILED)
					{
						//failed polling.
						printf("FATAL ERROR\r\n");
						closeAllHandles(ProcInfo, timeout_exceeded, lpHandles);
						finalizeRun(FALSE, hInFile, hOutFile);
						return 1;
					}
				}
			}
			else if (retVal == WAIT_FAILED)
			{
				//failed waiting on multiple objects. 
				printf("FATAL ERROR\r\n");
				closeAllHandles(ProcInfo, timeout_exceeded, lpHandles);
				finalizeRun(FALSE, hInFile, hOutFile);
				return 1;
			}
		}

		//Print Output
		fprintf(hOutFile, "%s", line);
		for (k=0; k<NUM_OF_EXES; k++)
		{
			GetExitCodeProcess(ProcInfo[k].hProcess, &lpExitCode);
			if(lpExitCode != 0)
			{
				passFlag = FALSE;
			}
			fprintf(hOutFile, " %s:%s", exes[k], (timeout_exceeded[k] ? timeout_str : output_strs[lpExitCode*-1]));
		}
		fprintf(hOutFile, "\n");
		

		//close all handles
		closeAllHandles(ProcInfo, timeout_exceeded, lpHandles);
	}

	endTime = clock();
	elapsed_time = (endTime - startTime);
	printf("The program ran for %ld c.c.\n", elapsed_time);

	finalizeRun(passFlag, hInFile, hOutFile);
	
	return 0;
}